// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: Ni_VcpuInterface.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2005 Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include "Ni/SAM_Ni_System.h"
#include "StdMacros.h"
#include "Blaze_CBInterface.h"

namespace Sam {
#include "cpu_lib_intf.h"
#include "vtracer.h"
}

#include <sstream>

using namespace std;
using namespace Riesling;


// keep global tables for ni cpu's 
const int NSYS_MAX  = 4; 
const int NVCPU_MAX = NSYS_MAX * 32; 



Sam_Ni_System *g_ni_sys[NSYS_MAX]; 
int g_nsys_max = 0; 

class Ni_Vcpu; 
Ni_Vcpu *g_ni_vcpu[NVCPU_MAX];
int g_ni_vcpu_max = 0; 

static void ni_trace_instr (uint32_t cpuid, uint64_t pc_va,
                            uint64_t pc_pa, uint64_t npc_va, uint32_t iw,
                            uint8_t an);
static void ni_handle_trap (uint32_t id, uint32_t tno, uint64_t tr_pc,
                            uint64_t tr_npc);
static void ni_handle_write (uint32_t id, uint64_t va, uint64_t pa, uint64_t data, 
			    int size, uint8_t byteMask);
static uint64_t ni_handle_read (uint32_t cpuid, uint32_t memop, uint64_t va, uint64_t pa,
			    int size);


// global methods to access system info
extern "C" uint_t vcpu_get_sys_type()
{
  union
  { 
    char name[4];
    uint_t type;
  }
  u;
  
  u.name[0] = 'n';
  u.name[1] = '1';
  u.name[2] = 'r';
  u.name[3] = 's';
  return u.type;
}

extern "C" uint_t vcpu_get_sys_size()
{
  return g_nsys_max;
}

extern "C" void* vcpu_get_sys_pntr( uint_t i )
{
  assert(i < g_nsys_max);
  return (void*)g_ni_sys[i];
}


////////////////////////////////////////////////////////////
//
// vcpu wrapper for a strand
//
class Ni_Vcpu : public Sam::Vcpu
{
  public:
    
    // constructor
    Ni_Vcpu(Ni_Strand * s)  { strand = s; }
    ~Ni_Vcpu() {}


    // methods
    int set_id( int id )  { strand->setId(id); return 0; }
    int get_id()   { return strand->getId(); }

    int stepi( int64_t n=1 );

    int interrupt( Sam::VCPU_InterruptRequest* i );  

    // read registers
    // returns a success code: false=>error
    int get_ireg (int wp, int regnum, uint64_t & regvalue);
    int get_freg (int issingle, int regnum, uint64_t & regvalue);

    
    /* the get_pr, get_hpr, get_asr, get_trapreg calls returns false if the register is unimplemented */

    // for trap-level specific registers, get/set_pr functions use the current trap_level
    int get_pr(int regnum, uint64_t & value);
    int get_hpr(int regnum, uint64_t & value);
    int get_asr(int regnum, uint64_t & value);
    int get_trapreg(int tl, int regnum, uint64_t & value);

    // write registers
    int set_ireg (int wp, int regnum, uint64_t value);
    int set_freg (int issingle, int regnum, uint64_t value);

    
    // for trap-level specific registers, get/set_pr functions use the current trap_level
    int set_pr(int regnum, uint64_t value);
    int set_hpr(int regnum, uint64_t value);
    int set_asr(int regnum, uint64_t value);
    int set_trapreg(int tl, int regnum, uint64_t value);
    
    // asi mapped regs
    int get_asi(uint8_t asi, uint64_t addr, uint64_t &data) { return 1; } 
    int set_asi(uint8_t asi, uint64_t addr, uint64_t  data) { return 1; }

    // caller must explicitly FREE return_array before it is overwritten or goes out of scope
    int get_tlb_entries(struct Sam::TLBInfo * &return_array) { return 0; }

  
    int read_mem ( uint64_t addr, uint64_t *value, int size, int asi= 0x82, int is_physical=0 ) { return 1; }
    int write_mem ( uint64_t addr, uint64_t value, int size, int asi=0x82, int is_physical=0 )  { return 1; }
    int set_breakpoint ( int *bp_id, Sam::VCPU_BpType type,  uint64_t value, Sam::VCPU_BpActionFn action, uint64_t mask= ~(uint64_t(0))) { return 1; } 

    int delete_breakpoint ( int bp_id = ~(0) ) { return 1; }
     

    // print breakpoint list to a file/consol
    int print_breakpoints ( FILE *fp ) { return 1; }
    
    // print all regs to file/consol
    int print_regs ( FILE *fp ) { return 1; }
    
    // print tlbs to file/consol
    int print_tlbs ( FILE *fp ) { return 1; }

    // internal data

    Ni_Strand* strand;    
     
    Sam::VCPU_Instruction instr;   // instruction record
    Sam::VCPU_Trap        trap;    // trap record

    uint64_t last_ld_va;	// load virtual address generated by the last instruction executed
    uint64_t last_ld_pa;	// load physical address generated by the last instruction executed
    uint64_t last_st_va;	// store virtual address generated by the last instruction executed
    uint64_t last_st_pa;	// store physical address generated by the last instruction executed


};  // Ni_Vcpu


//////////////////////////////////////////////////
//
// step nn instructions
//
int Ni_Vcpu::stepi( int64_t nn )  
{ 

   if (this->config.delay >= nn) 
   {
      this->config.delay -= nn;
      return 0;
   }
   else 
   {
      int ii = nn - this->config.delay;
      this->config.delay = 0;
      
      int status =  this->strand->step(ii);
      
      // Ni_Strand increments tick after every step. stick is an alias of tick
      //if (strand->getId() == 0) 
      //{
      //    Ni_ArchState *ni_state =  (Ni_ArchState*)strand->getArchStatePtr();
      //    uint64_t tick = ni_state->getTickRegPtr()->getCOUNTER() + ii;
      //    ni_state->getTickRegPtr()->setCOUNTER(tick);
      //    ni_state->getStickRegPtr()->setCOUNTER(tick);
      //}



      return status; 
   } 
}

/////////////////////////////////////////////////////////
//
// signal an interrupt
//
int Ni_Vcpu::interrupt( Sam::VCPU_InterruptRequest* i )
{ 
    //strand->signalIntp(i->data[0]); 
    return 0; 
}


/////////////////////////////////////////////////////////////////
int Ni_Vcpu::get_ireg (int wp, int regnum, uint64_t & regvalue)
{

      // wp = 0 (-1) REGULAR
      //      1 ALTERNATE
      //      2 VECTOR OR MMU
      //      3 VECTOR OR MMU


      Ni_ArchState * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();
      RegisterFile * rf        = ni_state->getRegisterFilePtr();
      
      if  (regnum < 0) 
      {
            fprintf (stderr, "reg access: negative reg ID (regnum = %d)\n", regnum);
            return 0;
      }

      if (regnum - 7 <= 7) 
      {  
          switch (wp) 
          {
              case Sam::VCPU_ACC_CUR:
              case Sam::VCPU_ACC_RG:  wp = 0; break;
              case Sam::VCPU_ACC_AG:  wp = 3; break;
              case Sam::VCPU_ACC_VG:  wp = 2; break;
              case Sam::VCPU_ACC_MG:  wp = 1; break; 
              default: 
                   fprintf (stderr, "reg access: unknown global set (%d), regnum = %d\n", wp, regnum);
                   return 1;

          }
          
          regvalue = rf->get (wp, regnum);
          return 0; 
      }
      else if (regnum <= 31) 
      {
          if (wp == Sam::VCPU_ACC_CUR)
              wp = 0;

          regvalue = rf->get (wp, regnum);
          return 0; 
      }
      else 
      {
          fprintf (stderr, "reg access: wrong reg ID (regnum = %d)\n", regnum);
          return 1;
      }

}

//////////////////////////////////////////////////////
int Ni_Vcpu::get_freg (int issingle, int regnum, uint64_t & value)
{
      Ni_ArchState      * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();
      FloatRegisterFile * rf        = ni_state->getFloatRegisterFilePtr();
      
      value =  issingle ? rf->getSpfp (regnum) : rf->getDpfp (regnum);
      return 0; 
}




//////////////////////////////////////////////////////////////////////
//
// for trap-level specific registers, get/set_pr functions use the current trap_level
// this code mirrors the rdpr emulation code 
//
int Ni_Vcpu::get_pr(int regnum, uint64_t & value)
{

    Ni_ArchState * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();
    int trap_level = 0;
     
    switch(regnum) 
    {
    case Sam::VCPU_PR_TPC:
        trap_level = (ni_state->getTrapLevelRegPtr())->getNative();
        if (trap_level == 0) return 1;
        value = (ni_state->getTpcRegPtr())->getTpc(trap_level);
        return 0;
    case Sam::VCPU_PR_TNPC:
        trap_level = (ni_state->getTrapLevelRegPtr())->getNative();
        if (trap_level == 0) return 1;
        value = (ni_state->getTnpcRegPtr())->getTnpc(trap_level);
        return 0;
    case Sam::VCPU_PR_TSTATE: {
        trap_level = (ni_state->getTrapLevelRegPtr())->getNative();
        if (trap_level == 0) return 1;
        Hv_TstateEntry te = (ni_state->getTstateRegPtr())->getTstateEntry(trap_level);
        value = te.getNative();
        return 0;             }
    case Sam::VCPU_PR_TT:
        trap_level = (ni_state->getTrapLevelRegPtr())->getNative();
        if (trap_level == 0) return 1;
        value = (ni_state->getTrapTypeRegPtr())->getTrapType(trap_level);
        return 0;
    case Sam::VCPU_PR_TICK:
        value = (ni_state->getTickRegPtr())->getNative();
        return 0;
    case Sam::VCPU_PR_TBA:
        value = (ni_state->getTbaRegPtr())->getNative();
        return 0;
    case Sam::VCPU_PR_PSTATE:
        value = (ni_state->getPstateRegPtr())->getNative();
        return 0;
    case Sam::VCPU_PR_TL:
        value = (ni_state->getTrapLevelRegPtr())->getNative();
        return 0;
    case Sam::VCPU_PR_PIL:
        value = (ni_state->getPilRegPtr())->getNative();
        return 0;
    case Sam::VCPU_PR_CWP:
        value = 0;
        return 1;
    case Sam::VCPU_PR_CANSAVE:
        value = 0;
        return 1;
    case Sam::VCPU_PR_CANRESTORE:
        value = 0;
        return 1;
    case Sam::VCPU_PR_CLEANWIN:
        value = 0;
        return 1;
    case Sam::VCPU_PR_OTHERWIN:
        value = 0;
        return 1;
    case Sam::VCPU_PR_WSTATE:
        value = (ni_state->getWstateRegPtr())->getNative();;
        return 0;
    case Sam::VCPU_PR_VER:
        value = (ni_state->getVerRegPtr())->getNative();
        return 0;

    default:
        return 1;
    } // switch(regnum)
}

///////////////////////////////////////////////////////
int Ni_Vcpu::get_hpr(int regnum, uint64_t & value)
{
  // TODO: not implemented 
  return 1;
} // bool CCpu::get_hpr(int regnum, uint64_t & value)



///////////////////////////////////////////////////
// this mirrors the rdasr instr
int Ni_Vcpu::get_asr(int regnum, uint64_t & value)
{
    Ni_ArchState * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();

    switch(regnum) 
    {
    case Sam::VCPU_ASR_Y:
        value = (ni_state->getYRegPtr())->getNative();
        return 0;
    case Sam::VCPU_ASR_CCR:
        value = (ni_state->getCcrRegPtr())->getNative();
        return 0;
    case Sam::VCPU_ASR_ASI:
        value = (ni_state->getAsiRegPtr())->getNative();
        return 0;
    case Sam::VCPU_ASR_TICK:
        value = (ni_state->getTickRegPtr())->getNative();
        return 0;
    case Sam::VCPU_ASR_PC:
        value = ni_state->getPc();
        return 0;
    case Sam::VCPU_ASR_FPRS:
        value = (ni_state->getFprsRegPtr())->getNative();
        return 0;
     
    case Sam::VCPU_ASR_TICK_CMPR:
        value = 0;
        return 1;
    case Sam::VCPU_ASR_STICK_REG:
        value = (ni_state->getStickRegPtr())->getNative();
        return 0;
  
    case Sam::VCPU_ASR_STICK_CMPR:
        value = 0;
        return 1;

    case Sam::VCPU_ASR_NPC:
        value = ni_state->getNpc(); 
        return 0; 
    case Sam::VCPU_ASR_FSR:
        value = (ni_state->getFsrRegPtr())->getNative();
        return 0; 

    default:
        return 1;
    } // switch regnum
}

///////////////////////////////////////////////////////////////////////////////
// the values used for regnum are the PR identifiers used in the rdpr instr
int Ni_Vcpu::get_trapreg(int tl, int regnum, uint64_t & value) 
{
    Ni_ArchState * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();
    
    switch(regnum) 
    {
    case Sam::VCPU_PR_TPC:
        value = (ni_state->getTpcRegPtr())->getTpc(tl);
        return 0;
    case Sam::VCPU_PR_TNPC:
        value = (ni_state->getTpcRegPtr())->getTpc(tl);
        return 0;
    case Sam::VCPU_PR_TSTATE: {
        Hv_TstateEntry te = (ni_state->getTstateRegPtr())->getTstateEntry(tl);
        value = te.getNative();
        return 0;             }
    case Sam::VCPU_PR_TT:
        value = (ni_state->getTrapTypeRegPtr())->getTrapType(tl);
        return 0;
    default:
        return 1;
    } // switch(regnum)
}



// write registers
int Ni_Vcpu::set_ireg (int wp, int regnum, uint64_t value) 
{ 
    return 1; 
}
int Ni_Vcpu::set_freg (int issingle, int regnum, uint64_t value) 
{ 
    return 1; 
}

    
// for trap-level specific registers, get/set_pr functions use the current trap_level
int Ni_Vcpu::set_pr(int regnum, uint64_t value)  
{ 
    return 1; 
}
int Ni_Vcpu::set_hpr(int regnum, uint64_t value) 
{ 
    return 1; 
}

//////////////////////////////////////////////////////////////
int Ni_Vcpu::set_asr(int regnum, uint64_t value) 
{ 
   Ni_ArchState * ni_state  = (Ni_ArchState*)strand->getArchStatePtr();

   switch(regnum) 
   {
   case Sam::VCPU_ASR_Y:   (ni_state->getYRegPtr())->setNative(value); return 0;
   case Sam::VCPU_ASR_PC:  ni_state->setPc(value);  return 0; 
   case Sam::VCPU_ASR_NPC: ni_state->setNpc(value); return 0;
   default: return 1; 
   }
}

///////////////////////////////////////////////////////////////
int Ni_Vcpu::set_trapreg(int tl, int regnum, uint64_t value) 
{ 
    return 1; 
}






/////////////////////////////////////////////////////////////////////////////
//
// create a vcpu wrapper for Ni strand
//
static void* ni_create( Sam::VCPU_Config* config, Sam::VCPU_ImpIntf* interface )/*{{{*/
{
     Ni_Vcpu *vcpu = NULL;  

     // last created ni cpu(system)
     int last_sys = g_nsys_max - 1; 
     if (last_sys < 0 ) last_sys = 0; 
     Sam_Ni_System *ni_sys = g_ni_sys[last_sys];

     if ((ni_sys==NULL) || (g_ni_vcpu_max > g_nsys_max * 32 - 1) ) 
     {
         // need to allocate another Ni system
         if ( g_nsys_max > NSYS_MAX )
         {
            fprintf(stderr, "ERROR: number of NI cpu's exceeds the limit  %i \n", NSYS_MAX);
            exit(1);    
         }
    
         // create a new Ni cpu
         ni_sys = new Sam_Ni_System (interface);

         BlazeCBInterface::setblazetraceinstrcb(ni_trace_instr);
         BlazeCBInterface::setblazehandletrapcb(ni_handle_trap);
	 BlazeCBInterface::setblazewritecb(ni_handle_write);
	 BlazeCBInterface::setblazereadcb(ni_handle_read);
         BlazeCBInterface::cosim(false);


         // set cpu id
         // ni_sys->setCpuId(g_nsys_max);

         g_ni_sys[g_nsys_max] = ni_sys; 
         g_nsys_max++; 
     }

     int id = g_ni_vcpu_max & 0x1f; // 32 strands per each cpu 

     Ni_Cpu *ni_cpu = (Ni_Cpu *) ni_sys->getCpuPtr(0);
     Ni_Core *ni_core = (Ni_Core *) ni_cpu->getCorePtr(id >> 2);
     Ni_Strand *ni_strand = (Ni_Strand *) ni_core->getStrandPtr(id & 0x3);
     ni_strand->setId(id);
     ni_strand->sendResumeMessage(); // make strand active

     Ni_ArchState *ni_state = (Ni_ArchState*) ni_strand->getArchStatePtr();
     ni_state->getPstateRegPtr()->setPRIV(1);
     ni_state->getPstateRegPtr()->setRED(1);
     ni_state->getPstateRegPtr()->setAG(1);
     ni_state->getPstateRegPtr()->setPEF(1);
     ni_state->getFprsRegPtr()->setFEF(1);

     // create a vcpu wrapper
     vcpu = new Ni_Vcpu ( ni_strand ); 
     g_ni_vcpu[g_ni_vcpu_max] = vcpu; 
     g_ni_vcpu_max++;

     // keep config info and sys interface
     vcpu->config   = *config; 
     vcpu->sys_intf = *interface;


    
    return (void *)vcpu;

}
/*}}}*/


/////////////////////////////////////////
//
// destroy all Ni cpu's
//
static int ni_destroy()/*{{{*/
{

    for ( int i = 0; i<g_nsys_max; i++)
    {
         delete g_ni_sys[i];
    } 
    g_nsys_max  = 0;
    g_ni_vcpu_max = 0;

    return 0;

}
/*}}}*/



static int ni_reset( uint64_t pc )/*{{{*/
{
    fprintf(stderr, "ERROR: Ni reset is not implemented \n");
    return 1;
}
/*}}}*/


static string intToString(int num) /*{{{*/
{
  ostringstream myStream; //creates an ostringstream object
  myStream << num << flush;

  /*
   * outputs the number into the string stream and then flushes
   * the buffer (makes sure the output is put into the stream)
   */
  
  return(myStream.str()); //returns the string form of the stringstream object
}
/*}}}*/



static int ni_save_restore( const char* dir_name, bool load )/*{{{*/
{

    for(int sys=0; sys<g_nsys_max; sys++) {
	FILE *fp; 
	string fname = string(dir_name) + "/Ni_Riesling.snapshot.sys" + intToString(sys);
	if (load) {
	    if ((fp = fopen (fname.c_str(), "r")) == NULL) {
		perror (fname.c_str());
		return 0;
	    }
	} else {
	    if ((fp = fopen (fname.c_str(), "w")) == NULL) {
		perror (fname.c_str());
		return 0;
	    }
	}

	SS_SnapShot ss(fp, load);
	g_ni_sys[sys]->snapshot(ss, "Rs_Ni");

	fclose(fp);
    }
    return 1;
}
/*}}}*/

static int ni_save( char* dir_name )/*{{{*/
{ 
    const bool load = false;	// false for save, true for restore
    return ni_save_restore(dir_name, load);
}
/*}}}*/

static int ni_restore( char* dir_name )/*{{{*/
{
    const bool load = true;	// false for save, true for restore
    return ni_save_restore(dir_name, load);
}
/*}}}*/


extern "C" int get_ex_interface( Sam::VCPU_ExInterface* interface )
{
  interface->create  = ni_create; 
  interface->destroy = ni_destroy;
  interface->reset   = ni_reset; 
  interface->save    = ni_save; 
  interface->restore = ni_restore; 



  return 0;
}


///////////////////////////////////////////////////////////////////////////
//
// Tracer interface
//


void ni_trace_instr (uint32_t cpuid, uint64_t pc_va,
                            uint64_t pc_pa, uint64_t npc_va, uint32_t iw,
                            uint8_t an)
{
    if(g_ni_vcpu[cpuid] &&
       g_ni_vcpu[cpuid]->config.trace_on &&
       g_ni_vcpu[cpuid]->sys_intf.vtrace)
    {
        // collect trace record
        Sam::VCPU_Instruction *i = &(g_ni_vcpu[cpuid]->instr);
        Ni_Strand *strand = g_ni_vcpu[cpuid]->strand;
        
        InstructionEmulator  *ie    = g_ni_vcpu[cpuid]->strand->getInstructionEmulator();
        
        i->nregs  = 0;
        i->ld_num = 0; 
        i->st_num = 0;
        i->itype  = Sam::VCPU_UNKNOWN_ITYPE; 


        i->cpuid = cpuid; 
        i->pc_va = pc_va; 
        i->pc_pa = pc_pa; 
        i->npc_va = npc_va; 
        i->opcode = iw; 
        i->annul  = an; 
        i->taken  = ie->get_ctiTaken(); //(npc_va != (pc_va + 4)); 
	i->ld_ea_va = g_ni_vcpu[cpuid]->last_ld_va;
	i->st_ea_va = g_ni_vcpu[cpuid]->last_st_va;
	i->ld_ea_pa = g_ni_vcpu[cpuid]->last_ld_pa;
	i->st_ea_pa = g_ni_vcpu[cpuid]->last_st_pa;

	Ni_ArchState *ni_state = strand->getArchStatePtr();
	i->pstate  = ni_state->getPstateRegPtr()->getNative();
	i->hpstate = ni_state->getNiHpstateRegPtr()->getNative();

        uint32_t               tl    = ie->get_TL();
	Ni_Mmu           *pmmu       = strand->getMmuPtr();
	Ni_MmuState      *pmmu_state = pmmu->getMmuStatePtr();
	uint16_t ctxt = pmmu_state->getPrimaryDContext();// pctxt.getNative ();
        i->icontext = (tl > 0) ? 0 :ctxt;

        // call tracer
        if (an == 0) // insruction was not annuled
            g_ni_vcpu[cpuid]->sys_intf.vtrace->instr(i);

        //call tracer
        if (i->exception)
        {
            Sam::VCPU_Trap *t = &(g_ni_vcpu[cpuid]->trap);
            g_ni_vcpu[cpuid]->sys_intf.vtrace->trap(t); 
         }

        i->exception = 0;

    }
    return; 
}
void ni_handle_trap (uint32_t id, uint32_t tno, uint64_t tr_pc,
                            uint64_t tr_npc)
{
    if(g_ni_vcpu[id] &&
       g_ni_vcpu[id]->config.trace_on &&
       g_ni_vcpu[id]->sys_intf.vtrace)
    {

       // collect trap record
       g_ni_vcpu[id]->instr.exception = 1;
       
       Sam::VCPU_Trap *t = &(g_ni_vcpu[id]->trap);

       t->pc_va  = tr_pc;
       t->npc_va = tr_npc;
       t->tno    = tno; 
       t->cpuid  = id; 

       
    }

    return; 
}

static uint64_t ni_handle_read (uint32_t id, uint32_t memop, uint64_t va, uint64_t pa, int size)
{
    if (id > g_ni_vcpu_max) {
	fprintf(stderr, "ERROR: memory read id %d exceeds the limit  %d \n", id, g_ni_vcpu_max);
	exit(1);    
    }
    g_ni_vcpu[id]->last_ld_va = va;
    g_ni_vcpu[id]->last_ld_pa = pa;
    return 0;	// not sure what this should return
}

static void ni_handle_write (uint32_t id, uint64_t va, uint64_t pa, uint64_t data, int size, uint8_t byteMask)
{
    if (id > g_ni_vcpu_max) {
	fprintf(stderr, "ERROR: memory write id %d exceeds the limit  %d \n", id, g_ni_vcpu_max);
	exit(1);    
    }

    g_ni_vcpu[id]->last_st_va = va;
    g_ni_vcpu[id]->last_st_pa = pa;
}

